package icraf.carbon.model;

import icraf.carbon.core.LandCover;
import icraf.carbon.core.LandCoverList;
import icraf.carbon.core.Project;
import icraf.carbon.core.Zone;
import icraf.carbon.core.ZoneList;
import icraf.carbon.core.Zone.DataType;

import java.util.HashMap;

public class AbacusModel {

	public static Project runLanduseMatrixSimulation(Project p,
			int numberOfCycles, boolean modifyProbMatrix) {
		if (modifyProbMatrix) {
			p = normalizeProbMatrix(p);
		}
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int c = 0; c < numberOfCycles; c++) {
			for (int i = 0; i < zones.getSize(); i++) {
				Zone z = zones.get(i);
				// lc value per rows
				HashMap<LandCover, Double> vRows = new HashMap<LandCover, Double>();
				// lc value per columns
				HashMap<LandCover, Double> vColumns = new HashMap<LandCover, Double>();
				for (int j = 0; j < lcList.size(); j++) {
					double vRow = 0;
					double vColumn = 0;
					for (int k = 0; k < lcList.size(); k++) {
						vRow += z.getValue(lcList.get(j), lcList.get(k),
								DataType.LANDCOVER_CHANGE, null);
						vColumn += z.getValue(lcList.get(k), lcList.get(j),
								DataType.LANDCOVER_CHANGE, null);
					}
					vRows.put(lcList.get(j), vRow);
					vColumns.put(lcList.get(j), vColumn);
				}
				double sum = 0;
				for (int j = 0; j < lcList.size(); j++) {
					for (int k = 0; k < lcList.size(); k++) {
						double d2 = 0;
						if (modifyProbMatrix) {
							double prob = z.getValue(lcList.get(j),
									lcList.get(k),
									DataType.TPM_SCENARIO, null);
							d2 = prob * vColumns.get(lcList.get(j));
						} else {
							double d1 = z.getValue(lcList.get(j),
									lcList.get(k), DataType.LANDCOVER_CHANGE, null);
							d2 = d1 * vColumns.get(lcList.get(j))
									/ vRows.get(lcList.get(j));
						}
						d2 = Double.isNaN(d2) ? 0 : d2;
						z.setValue(d2, lcList.get(j), lcList.get(k),
								DataType.LANDCOVER_CHANGE, null);
						sum += d2;
					}
				}

				// normalize
				for (int j = 0; j < lcList.size(); j++) {
					for (int k = 0; k < lcList.size(); k++) {
						double d1 = z.getValue(lcList.get(j), lcList.get(k),
								DataType.LANDCOVER_CHANGE, null);
						double d2 = d1 / sum;
						d2 = Double.isNaN(d2) ? 0 : d2;
						z.setValue(d2, lcList.get(j), lcList.get(k),
								DataType.LANDCOVER_CHANGE, null);
					}
				}
			}
		}
		return p;
	}

	public static Project updateTransitionProbabilityMatrix(Project p) {
		// TransitionProbabilityMatrix tpm = new TransitionProbabilityMatrix();
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int i = 0; i < zones.getSize(); i++) {
			Zone z = zones.get(i);
			// lc value per rows
			HashMap<LandCover, Double> vRows = new HashMap<LandCover, Double>();
			for (int j = 0; j < lcList.size(); j++) {
				double vRow = 0;
				for (int k = 0; k < lcList.size(); k++) {
					vRow += z.getValue(lcList.get(j), lcList.get(k),
							DataType.LANDCOVER_CHANGE, null);
				}
				vRows.put(lcList.get(j), vRow);
			}
			for (int j = 0; j < lcList.size(); j++) {
				for (int k = 0; k < lcList.size(); k++) {
					double d1 = z.getValue(lcList.get(j), lcList.get(k),
							DataType.LANDCOVER_CHANGE, null);
					double d2 = d1 / vRows.get(lcList.get(j));
					d2 = Double.isNaN(d2) ? 0 : d2;
					z.setValue(d2, lcList.get(j), lcList.get(k),
							DataType.TPM_SCENARIO, null);
				}
			}
		}
		p = updateLanduseScenarioByTPM(p);
		return p;
	}

	private static Project normalizeProbMatrix(Project p) {
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int i = 0; i < zones.getSize(); i++) {
			Zone z = zones.get(i);
			for (int j = 0; j < lcList.size(); j++) {
				double sum = 0;
				for (int k = 0; k < lcList.size(); k++) {
					sum += z.getValue(lcList.get(j), lcList.get(k),
							DataType.TPM_SCENARIO, null);
				}
				for (int k = 0; k < lcList.size(); k++) {
					double d1 = z.getValue(lcList.get(j), lcList.get(k),
							DataType.TPM_SCENARIO, null);
					double d2 = d1 / sum;
					d2 = Double.isNaN(d2) ? 0 : d2;
					z.setValue(d2, lcList.get(j), lcList.get(k),
							DataType.TPM_SCENARIO, null);
				}
			}
		}
		return p;
	}

	public static Project updateTPMbyLanduseScenario(Project p) {
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int i = 0; i < zones.getSize(); i++) {
			Zone z = zones.get(i);
			// lc value per columns
			HashMap<LandCover, Double> vColumns = new HashMap<LandCover, Double>();
			for (int j = 0; j < lcList.size(); j++) {
				double vColumn = 0;
				for (int k = 0; k < lcList.size(); k++) {
					vColumn += z.getValue(lcList.get(k), lcList.get(j),
							DataType.LANDCOVER_CHANGE, null);
				}
				vColumns.put(lcList.get(j), vColumn);
			}
			for (int j = 0; j < lcList.size(); j++) {
				for (int k = 0; k < lcList.size(); k++) {
					double d2 = z.getValue(lcList.get(j), lcList.get(k),
							DataType.LANDUSE_SCENARIO, null);
					double prob = d2 / vColumns.get(lcList.get(j));
					 prob = Double.isNaN(prob) ? 0 : prob;
						z.setValue(prob, lcList.get(j), lcList.get(k),
								DataType.TPM_SCENARIO, null);
				}
			}
		}
		return normalizeProbMatrix(p);
	}

	public static Project updateLanduseScenarioByTPM(Project p) {
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int i = 0; i < zones.getSize(); i++) {
			Zone z = zones.get(i);
			// lc value per columns
			HashMap<LandCover, Double> vColumns = new HashMap<LandCover, Double>();
			for (int j = 0; j < lcList.size(); j++) {
				double vColumn = 0;
				for (int k = 0; k < lcList.size(); k++) {
					vColumn += z.getValue(lcList.get(k), lcList.get(j),
							DataType.LANDCOVER_CHANGE, null);
				}
				vColumns.put(lcList.get(j), vColumn);
			}
			double sum = 0;
			for (int j = 0; j < lcList.size(); j++) {
				for (int k = 0; k < lcList.size(); k++) {

					double prob = z.getValue(lcList.get(j), lcList.get(k),
							DataType.TPM_SCENARIO, null);
					double d2 = prob * vColumns.get(lcList.get(j));
					d2 = Double.isNaN(d2) ? 0 : d2;
					z.setValue(d2, lcList.get(j), lcList.get(k),
							DataType.LANDUSE_SCENARIO, null);
					sum += d2;

				}
			}
			// normalize
			for (int j = 0; j < lcList.size(); j++) {
				for (int k = 0; k < lcList.size(); k++) {
					double d1 = z.getValue(lcList.get(j), lcList.get(k),
							DataType.LANDUSE_SCENARIO, null);
					double d2 = d1 / sum;
					d2 = Double.isNaN(d2) ? 0 : d2;
					z.setValue(d2, lcList.get(j), lcList.get(k),
							DataType.LANDUSE_SCENARIO, null);
				}
			}

		}
		return p;
	}
	
	public static Project copyMatrix(Project p, DataType source, DataType target) {
		LandCoverList lcList = p.getLandCoverList();
		ZoneList zones = p.getZoneList();
		for (int i = 0; i < zones.getSize(); i++) {
			Zone z = zones.get(i);
			for (int j = 0; j < lcList.size(); j++) {
				for (int k = 0; k < lcList.size(); k++) {
					double d = z.getValue(lcList.get(j), lcList.get(k), source,
							null);
					z.setValue(d, lcList.get(j), lcList.get(k), target, null);
				}
			}
		}
		return p;
	}
}
